package org.jeecg.modules.es.service.impl;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import org.elasticsearch.action.admin.indices.get.GetIndexRequest;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.jeecg.modules.article.entity.Article;
import org.jeecg.modules.es.service.ElasticSearchService;
import org.jeecg.modules.es.vo.KeywordsVO;
import org.jeecg.modules.es.vo.KnowledgeVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * TODO: es相关接口实现类
 *
 * @author Zhang Hao yu
 * @date 2020/12/2 19:31
 */
@Service
public class ElasticSearchServiceImpl implements ElasticSearchService {
    @Autowired
    RestHighLevelClient restHighLevelClient;


    @Override
    public boolean getIndexExists(String index) throws IOException {
        GetIndexRequest request = new GetIndexRequest().indices("kuang_index");
        boolean exists = restHighLevelClient.indices().exists(request, RequestOptions.DEFAULT);
        return exists;
    }

    @Override
    public boolean getDocumentExists(String index, String id) throws IOException {
        GetRequest getRequest = new GetRequest(index, id);

        return restHighLevelClient.exists(getRequest, RequestOptions.DEFAULT);
    }

    @Override
    public GetResponse getDocumentResponse(String index, String id) {
        GetRequest getRequest = new GetRequest(index, id);

        try {
            return restHighLevelClient.get(getRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public UpdateResponse updateDocument(String index, String id, Object object, String timeout) {
        UpdateRequest updateRequest = new UpdateRequest(index, id);
        updateRequest.timeout(timeout);
        updateRequest.doc(JSON.toJSONString(object), XContentType.JSON);
        UpdateResponse updateResponse = null;
        try {
            updateResponse = restHighLevelClient.update(updateRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return updateResponse;
    }

    @Override
    public DeleteResponse deleteDocument(String index, String id, String timeout) {
        DeleteRequest deleteRequest = new DeleteRequest(index, id);
        deleteRequest.timeout(timeout);

        DeleteResponse deleteResponse = null;
        try {
            deleteResponse = restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
        }

        return deleteResponse;
    }

    @Override
    public boolean bulkData(String globalIndex, String timeout, List<?> list) throws IOException {

        BulkRequest bulkRequest = new BulkRequest(globalIndex);
        bulkRequest.timeout(timeout);

        if (list.get(0) instanceof Article) {
            for (int i = 0; i < list.size(); i++) {
                bulkRequest.add(
                        new IndexRequest()
                                .id(((Article) (list.get(i))).getId())
                                .source(JSON.toJSONString(list.get(i)), XContentType.JSON));
            }
        } else if (list.get(0) instanceof KnowledgeVO) {
            for (int i = 0; i < list.size(); i++) {
                bulkRequest.add(
                        new IndexRequest()
                                .id(((KnowledgeVO) (list.get(i))).getId())
                                .source(JSON.toJSONString(list.get(i)), XContentType.JSON));
            }
        }

        BulkResponse bulkResponse = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
        return !bulkResponse.hasFailures();
    }


    @Override
    public List<Map<String, Object>> searchPage(String keywords, int pageNo, int pageSize, String index) {
        // 条件搜索
        SearchRequest searchRequest = new SearchRequest(index);
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

        // 分页
        sourceBuilder.from((pageNo - 1) * pageSize);
        sourceBuilder.size(pageSize);
        // 匹配  matchPhraseQuery 可以实现不分词模糊查询！！！！！！
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
//        MultiMatchQueryBuilder matchQuery = QueryBuilders.
//                multiMatchQuery(keywords, "articleTitle", "articleAuthor", "articleContent").
//                field("articleTitle", 5);
        MatchPhraseQueryBuilder articleTitleQuery = QueryBuilders.matchPhraseQuery("articleTitle", keywords);
        MatchPhraseQueryBuilder articleAuthorQuery = QueryBuilders.matchPhraseQuery("articleAuthor", keywords);
        MatchPhraseQueryBuilder articleContentQuery = QueryBuilders.matchPhraseQuery("articleContent", keywords);

        boolQueryBuilder.should(articleTitleQuery);
        boolQueryBuilder.should(articleAuthorQuery);
        boolQueryBuilder.should(articleContentQuery);

        sourceBuilder.query(boolQueryBuilder);

        sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));

        // 高亮
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("articleTitle");
        highlightBuilder.preTags("<span style='color:red'>");
        highlightBuilder.postTags("</span>");
//         禁止全部高亮
        highlightBuilder.requireFieldMatch(true);
        sourceBuilder.highlighter(highlightBuilder);

        // 搜索
        searchRequest.source(sourceBuilder);
        ArrayList<Map<String, Object>> list = new ArrayList<>();
        try {
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            for (SearchHit hit : searchResponse.getHits().getHits()) {
                // 获取高亮字段
                Map<String, HighlightField> highlightFields = hit.getHighlightFields();
                HighlightField articleTitle = highlightFields.get("articleTitle");
                // 原来的结果
                Map<String, Object> sourceAsMap = hit.getSourceAsMap();
                // 解析高亮字段
                if (articleTitle != null) {
                    Text[] fragments = articleTitle.fragments();
                    StringBuilder sb = new StringBuilder();
                    for (Text text : fragments) {
                        sb.append(text);
                    }
                    sourceAsMap.put("articleTitle", sb.toString());
                }
                list.add(sourceAsMap);
            }
/*            for (SearchHit hit : searchResponse.getHits().getHits()) {
                Map<String, Object> sourceAsMap = hit.getSourceAsMap();
                list.add(sourceAsMap);
            }*/
        } catch (IOException e) {
            e.printStackTrace();
        }
        list.forEach(res -> {
            Object createTime = res.get("createTime");
            // 修改时间戳为具体时间
            if (createTime != null) {
                res.put("createTime",
                        LocalDateTime.ofInstant(Instant.ofEpochMilli((Long) createTime),
                                ZoneId.systemDefault()).toLocalDate().toString());
            }
        });

        return list;
    }


    @Override
    public void insertSource(Article article, String index) throws IOException {
        // 创建请求
        IndexRequest request = new IndexRequest(index);

        // 设置规则 put  /kuang_index/_doc/1
        request.id(article.getId());
        // json数据
        request.source(JSON.toJSONString(article), XContentType.JSON);

        // 发送请求
        IndexResponse indexResponse = restHighLevelClient.index(request, RequestOptions.DEFAULT);

        System.out.println(indexResponse.toString());
    }

    @Override
    public void insertSource(KnowledgeVO knowledge, String index) throws IOException {
        // 创建请求
        IndexRequest request = new IndexRequest(index);

        // 设置规则 put  /kuang_index/_doc/1
        request.id(knowledge.getId());
        // json数据
        request.source(JSON.toJSONString(knowledge), XContentType.JSON);

        // 发送请求
        IndexResponse indexResponse = restHighLevelClient.index(request, RequestOptions.DEFAULT);

        System.out.println(indexResponse.toString());
    }

    @Override
    public List<Map<String, Object>> searchPageMajor(KeywordsVO keywords, Integer pageNo, Integer pageSize, String index) {
        String key = keywords.getKeywords();
        String author = keywords.getAuthor();
        String title = keywords.getTitle();
        String content = keywords.getContent();
        // 条件搜索
        SearchRequest searchRequest = new SearchRequest(index);
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

        // 分页
        sourceBuilder.from((pageNo - 1) * pageSize);
        sourceBuilder.size(pageSize);

        // 匹配  matchPhraseQuery 可以实现不分词模糊查询！！！！！！
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        if (StrUtil.isNotBlank(key)) {
            MatchPhraseQueryBuilder keywordQuery = QueryBuilders.matchPhraseQuery("keywordList", key);
            boolQueryBuilder.must(keywordQuery);
        }
        if (StrUtil.isNotBlank(author)) {
            MatchPhraseQueryBuilder authorQuery = QueryBuilders.matchPhraseQuery("knowledgeAuthor", author);
            boolQueryBuilder.must(authorQuery);
        }
        if (StrUtil.isNotBlank(title)) {
            MatchPhraseQueryBuilder titleQuery = QueryBuilders.matchPhraseQuery("knowledgeTitle", title);
            boolQueryBuilder.must(titleQuery);
        }
        if (StrUtil.isNotBlank(content)) {
            MatchPhraseQueryBuilder contentQuery = QueryBuilders.matchPhraseQuery("knowledgeContent", content);
            boolQueryBuilder.must(contentQuery);
        }
        sourceBuilder.query(boolQueryBuilder);
        sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));

        // 高亮
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("knowledgeTitle");
        highlightBuilder.preTags("<span style='color:red'>");
        highlightBuilder.postTags("</span>");
//         禁止全部高亮
        highlightBuilder.requireFieldMatch(true);
        sourceBuilder.highlighter(highlightBuilder);

        // 搜索
        searchRequest.source(sourceBuilder);
        ArrayList<Map<String, Object>> list = new ArrayList<>();
        try {
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            for (SearchHit hit : searchResponse.getHits().getHits()) {
                // 获取高亮字段
                Map<String, HighlightField> highlightFields = hit.getHighlightFields();
                HighlightField articleTitle = highlightFields.get("knowledgeTitle");
                // 原来的结果
                Map<String, Object> sourceAsMap = hit.getSourceAsMap();
                // 解析高亮字段
                if (articleTitle != null) {
                    Text[] fragments = articleTitle.fragments();
                    StringBuilder sb = new StringBuilder();
                    for (Text text : fragments) {
                        sb.append(text);
                    }
                    sourceAsMap.put("knowledgeTitle", sb.toString());
                }
                list.add(sourceAsMap);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        list.forEach(res -> {
            Object createTime = res.get("createTime");
            // 修改时间戳为具体时间
            if (createTime != null) {
                res.put("createTime",
                        LocalDateTime.ofInstant(Instant.ofEpochMilli((Long) createTime),
                                ZoneId.systemDefault()).toLocalDate().toString());
            }
        });
        return list;
    }
}